//
//  CSEncryptHelper.m
//  CSEncryptHelper
//
//  Created by luodp on 2018/5/4.
//

#import "CSEncryptHelper.h"

#define KEY_LENGTH 16
#define KEY_GROUP_COUNT 200

NSString * const messageHeaderHexStr = @"aa55";

@interface CSEncryptHelper ()

/**密钥组数量*/
@property (nonatomic,assign) NSUInteger keyGroupCount;

@end

@implementation CSEncryptHelper

+ (instancetype)sharedEncryptHelper {
    static CSEncryptHelper * encrypHelper = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        encrypHelper = [[self alloc]init];
    });
    return encrypHelper;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.keyGroupCount = KEY_GROUP_COUNT;
    }
    return self;
}

- (NSString *)selfPublic_key {
    NSString * public_key = [YYKeychain getPasswordForService:PUBLICKEY account:@"123456789"];
    NSLog(@"public_key:%@", public_key);
    return public_key;
    return @"";
}

- (NSString *)selfPrivate_key {
    NSString * private_Key = [YYKeychain getPasswordForService:PRIVATEKEY account:@"123456789"];
    return private_Key;
    return @"";
}

+ (NSDictionary *)generyKeyPair:(NSString *)account {
    return [[CSEncryptHelper sharedEncryptHelper] generyKeyPair: account];
}

- (NSDictionary *)generyKeyPair:(NSString *)account {
    
    NSDictionary * keyPairDic = [SM2Coded generyKeyPair];
    BOOL isSet = NO;
    isSet = [YYKeychain setPassword:keyPairDic[PUBLICKEY] forService:PUBLICKEY account:account];
    isSet ? isSet = NO : NSLog(@"public key set keychain error");
    isSet = [YYKeychain setPassword:keyPairDic[PRIVATEKEY] forService:PRIVATEKEY account:account];
    isSet ? isSet = NO : NSLog(@"private key set keychain error");
    return keyPairDic;
}

- (NSData *)getSessionData {
    
    Byte randomBuf[KEY_LENGTH] = {'\0'};
    //   1.生成16位随机数
    arc4random_buf(randomBuf, KEY_LENGTH);
    NSData * sessionData = [NSData dataWithBytes:randomBuf length:KEY_LENGTH];
    return sessionData;
}

- (NSData *)encrypt:(CSEncryptMessage *)message {
    if (message == nil) {
        return nil;
    }
    //    1. 报文头                                len 2
    NSMutableData * totalData = [NSMutableData data];
    [totalData appendData:[NSData dataWithHexString:messageHeaderHexStr]];
    //    2. 报文类型                               len 1
    [totalData appendData:[self messageType2byte:message.messageType]];
    //    3. 算法类型                               len 1
    [totalData appendData:[self algorithmType2byte:message.algorithmType]];
    //    4. 设备标识（sn）                          len 34
    
    NSData * selfIdentifierData = [NSData dataWithHexString:[self.selfIdentifier md5String]];
    NSData * otherIdentifierData = [NSData dataWithHexString:[message.otherIdentifier md5String]];
    int identifierLength = (int)(selfIdentifierData.length + otherIdentifierData.length);
    //    转换大小端
    identifierLength = htonl(identifierLength);
    //    长度
    [totalData appendData: [NSData dataWithBytes: &identifierLength length:sizeof(identifierLength)]];
    [totalData appendData:selfIdentifierData];
    [totalData appendData:otherIdentifierData];
    
    //    5. 数据域生成16位会话密钥
    NSData * sessionData = [self getSessionData];
    //    5.1 会话密钥1 (长度+接收方公钥对会话密钥加密)   len 116
    NSData * otherSessionKey = [SM2Coded sm2EncodeWithData:sessionData keyData:[NSData dataWithHexString:message.otherPublic_key] mode:SM2ModeC132];
    int otherSessionKeyLen = htonl(otherSessionKey.length);
    [totalData appendData:[NSData dataWithBytes:&otherSessionKeyLen length:sizeof(otherSessionKey)]];
    [totalData appendData:otherSessionKey];
    //    5.2 密文数据  (长度+会话密钥对明文加密)        len 4+n
    int dataLength = htonl(message.contentData.length);
    //    长度
    [totalData appendData:[NSData dataWithBytes:&dataLength length:sizeof(dataLength)]];
    //    密文
    [totalData appendData:[SM2Coded sm4EcbEncode:[self fillDataTo16:message.contentData] key:sessionData]];
    //    5.3 会话密钥2 (长度+发送方公钥对会话密钥加密)   len 116
    NSData * selfSessionKey = [SM2Coded sm2EncodeWithData:sessionData keyData:[NSData dataWithHexString:message.otherPublic_key] mode:SM2ModeC132];
    int selfSessionKeyLen = htonl(selfSessionKey.length);
    [totalData appendData:[NSData dataWithBytes:&selfSessionKeyLen length:sizeof(selfSessionKeyLen)]];
    [totalData appendData:selfSessionKey];
    //    5.4 司法公钥  (长度+司法公钥对会话密钥加密)     len 116
    NSData * judiciarySessionKey = [SM2Coded sm2EncodeWithData:sessionData keyData:[NSData dataWithHexString:self.judiciaryPublic_key] mode:SM2ModeC132];
    int judiciarySessionKeyLen = htonl(judiciarySessionKey.length);
    [totalData appendData:[NSData dataWithBytes:&judiciarySessionKeyLen length:sizeof(judiciarySessionKeyLen)]];
    [totalData appendData:judiciarySessionKey];
    //    6. 签名      (SM3校验值)                   len 32
    [totalData appendData:[SM2Coded sm3:totalData]];
    
    return nil;
}


- (CSEncryptMessage *)decrypt:(NSData *)data{
    if (data == nil || data.length < 38 ) {
        return nil;
    }
    
    NSData * sm3Data = [data subdataWithRange:NSMakeRange(data.length - 32, 32)];
    NSData * contentData = [data subdataWithRange:NSMakeRange(0, data.length - 32)];
    
    if (![sm3Data isEqualToData:[SM2Coded sm3:contentData]]) {
        NSLog(@"数据不完整");
        return nil;
    }
    
    int index = 0;
    CSEncryptMessage * message = [CSEncryptMessage new];
    //    1. 数据头
    NSData * messageHeader = [data subdataWithRange:NSMakeRange(index, 2)];
    if (![messageHeader isEqualToData:[NSData dataWithHexString:messageHeaderHexStr]]) {
        NSLog(@"密文格式错误");
        return nil;
    }
    index += 2;
    //    2. 报文类型
    NSData *messageTypeData = [data subdataWithRange:NSMakeRange(index, 1)];
    Byte messageType = 0x00;
    [messageTypeData getBytes:&messageType length:1];
    message.messageType = messageType;
    index += 1;
    //    3. 算法类型
    NSData * algorithmTypeData = [data subdataWithRange:NSMakeRange(index, 1)];
    Byte algorithmType = 0x00;
    [algorithmTypeData getBytes:&algorithmType length:1];
    message.algorithmType = algorithmType;
    index += 1;
    //    4. 设备标识  长度
    NSData * deviceIdentifierLenData = [data subdataWithRange:NSMakeRange(index, 4)];
    int deviceIdentifierLen = 0;
    [deviceIdentifierLenData getBytes:&deviceIdentifierLen length:deviceIdentifierLenData.length];
    index += 4;
    //    设备标识数据
    NSData * deviceIdentifierData = [data subdataWithRange:NSMakeRange(index, deviceIdentifierLen)];
    NSData * selfIdentifier = [deviceIdentifierData subdataWithRange:NSMakeRange(0, deviceIdentifierData.length/2)];
    NSData * otherIdentifier = [deviceIdentifierData subdataWithRange:NSMakeRange(deviceIdentifierData.length/2, deviceIdentifierData.length/2)];
    message.otherIdentifier = otherIdentifier;
    NSData * selfIdentifierData = [NSData dataWithHexString:[self.selfIdentifier md5String]];
    
    
    index += deviceIdentifierLen;
    //    接收方会话密钥长度
    NSData * reciverSesssionKeyLenData = [data subdataWithRange:NSMakeRange(index, 4)];
    int reciverSesssionKeyLen = 0;
    [reciverSesssionKeyLenData getBytes:&reciverSesssionKeyLen length:reciverSesssionKeyLenData.length];
    index += 4;
    //    接收方会话密钥
    NSData * reciverSessionKey = [data subdataWithRange:NSMakeRange(index, reciverSesssionKeyLen)];
    index += reciverSesssionKeyLen;
    //    密文数据长度
    int ciphertextLen = 0;
    [[data subdataWithRange:NSMakeRange(index, 4)] getBytes:&ciphertextLen length:sizeof(ciphertextLen)];
    index += 4;
    
    NSData * ciphertextData = [data subdataWithRange:NSMakeRange(index, ciphertextLen)];
    index += ciphertextLen;
    
    //    发送方会话密钥
    //    长度
    int senderSessionKeyLen = 0;
    [[data subdataWithRange:NSMakeRange(index, 4)] getBytes:&senderSessionKeyLen length:sizeof(senderSessionKeyLen)];
    index += 4;
    
    NSData * senderSessionKey = [data subdataWithRange:NSMakeRange(index, senderSessionKeyLen)];
    
    //    5. 判断是否为自己发出的
    NSData * sessionKey = nil;
    if ([selfIdentifier isEqualToData:selfIdentifierData]) {
        //        5.1 自己发出的则使用发送方会话密钥解密
        sessionKey = [SM2Coded sm2DecodeData:senderSessionKey keyData:[NSData dataWithHexString:self.selfPrivate_key] mode:SM2ModeC132];
        
    } else if ([otherIdentifier isEqualToData:selfIdentifier]) {
        //        5.2 别人发出的则使用接收方会话密钥解密
        sessionKey = [SM2Coded sm2DecodeData:reciverSessionKey keyData:[NSData dataWithHexString:self.selfPrivate_key] mode:SM2ModeC132];
    }
    //    6. 解密数据
    NSData * originFillData = [SM2Coded sm4dEcbDecode:ciphertextData key:sessionKey];
    NSData * originData = [self subFillData:originFillData];
    message.contentData = originData;
    return message;
}

- (NSData *)fillDataTo16:(NSData *)data {
    NSMutableData * fillData = [NSMutableData dataWithData:data];
    [fillData appendData:[@"。" dataUsingEncoding:NSUTF8StringEncoding]];
    //    如果 fillData.length % 16 == 0则不补齐0 了就
    while (fillData.length % 16) {
        [fillData appendData:[NSData dataWithHexString:@"00"]];
    }
    return fillData;
}

- (NSData *)subFillData:(NSData *)data {
    //    从 data 的后面往前查找19个字节，因为补0最多可能补16个字节。
    NSRange range = [data rangeOfData:[@"。" dataUsingEncoding:NSUTF8StringEncoding] options: NSDataSearchBackwards range:NSMakeRange(0, data.length)];
    
    if (range.location == NSNotFound) {
        return nil;
    }
    //    截取从开头到。的位置
    NSData * subData = [data subdataWithRange:NSMakeRange(0, range.location)];
    NSLog(@"%@", subData);
    return subData;
}

- (NSData *)algorithmType2byte:(AlgorithmType)algorithmType {
    switch (algorithmType) {
        case AlgorithmTypeSm4Sm2:{
            return [NSData dataWithHexString:@"02"];
        }
            break;
        case AlgorithmTypeRsa3des:
            return [NSData dataWithHexString:@"01"];
            break;
            
        default:
            break;
    }
}

- (NSData *)messageType2byte:(MessageType)messageType {
    
    switch (messageType) {
        case MessageTypeLow:
            return [NSData dataWithHexString:@"01"];
            break;
        case MessageTypeHeight:
            return [NSData dataWithHexString:@"02"];
            break;
        default:
            break;
    }
    
}


@end
